#include "binarytextedit.h"

#include <QTextStream>

BinaryTextEdit::BinaryTextEdit(EscapeMode escapeMode, QWidget *parent) :
    QPlainTextEdit(parent), _escapeMode(escapeMode)
{
    connect(this, SIGNAL(textChanged()), this, SLOT(sync()));
}
BinaryTextEdit::BinaryTextEdit(QWidget *parent) :
    QPlainTextEdit(parent), _escapeMode(CEscape)
{
    connect(this, SIGNAL(textChanged()), this, SLOT(sync()));
}

void BinaryTextEdit::setBinaryData(QByteArray data)
{
    if (_binaryData != data)
    {
        _binaryData = data;
        setPlainText(escape(data));
        emit binaryDataChanged();
    }
}

void BinaryTextEdit::setEscapeMode(EscapeMode mode)
{
    if (_escapeMode != mode)
    {
        _escapeMode = mode;
        setPlainText(escape(_binaryData));
        emit escapeModeChanged();
    }
}

QString BinaryTextEdit::escape(QByteArray data, EscapeMode escapeMode)
{
    QString text;
    switch (escapeMode)
    {
    case None:
        text = QString(data);
        break;
    case CEscape:
        foreach (const unsigned char byte, data) {
            if (byte < 0x20 || byte > 0x7e)
            {
                switch (byte)
                {
                case '\0':
                    text.append("\\0");
                    break;
                case '\a':
                    text.append("\\a");
                    break;
                case '\b':
                    text.append("\\b");
                    break;
                case '\t':
                    text.append("\\t");
                    break;
                case '\n':
                    text.append("\\n");
                    break;
                case '\v':
                    text.append("\\v");
                    break;
                case '\f':
                    text.append("\\f");
                    break;
                case '\r':
                    text.append("\\r");
                    break;
                default:
                    text.append("\\x").append(QString::number(byte, 16).prepend(byte < 16 ? "0" : ""));
                }
            }
            else if (byte == '\\')
            {
                text.append("\\\\");
            }
            else
            {
                text.append(byte);
            }
        }
        break;
    case URLEscape:
        text = QString(data.toPercentEncoding());
        break;
    case Hex:
        text = QString(data.toHex());
        break;
    case Base64:
        text = QString(data.toBase64());
        break;
    }
    return text;
}

QByteArray BinaryTextEdit::unescape(QString data, EscapeMode escapeMode)
{
    QByteArray text;
    switch (escapeMode)
    {
    case None:
        text = data.toAscii();
        break;
    case CEscape:
    {
        QTextStream stream(&data);
        QChar cc;
        while (!stream.atEnd())
        {
            stream >> cc;
            if (cc == '\\')
            {
                if (stream.atEnd()) break;
                stream >> cc;
                switch (cc.toAscii())
                {
                case '\\':
                    break;
                case '0':
                    cc = '\0';
                    break;
                case 'a':
                    cc = '\a';
                    break;
                case 'b':
                    cc = '\b';
                    break;
                case 't':
                    cc = '\t';
                    break;
                case 'n':
                    cc = '\n';
                    break;
                case 'v':
                    cc = '\v';
                    break;
                case 'f':
                    cc = '\f';
                    break;
                case 'r':
                    cc = '\r';
                    break;
                case 'x':
                {
                    char c1, c2;
                    if (stream.atEnd()) break;
                    stream >> c1;
                    if (stream.atEnd()) break;
                    stream >> c2;
                    cc = QByteArray::fromHex(QString(c1).append(c2).toAscii()).at(0);
                    break;
                }
                default:
                    qDebug("Sequenza di escape non valida: \\%c", cc.toAscii());
                }
            }
            text.append(cc.toAscii());
        }
        break;
    }
    case URLEscape:
        text = QByteArray::fromPercentEncoding(data.toAscii());
        break;
    case Hex:
        text = QByteArray::fromHex(data.toAscii());
        break;
    case Base64:
        text = QByteArray::fromBase64(data.toAscii());
        break;
    }
    return text;
}

void BinaryTextEdit::sync()
{
    QByteArray bd = unescape(toPlainText());
    if (bd != _binaryData)
    {
        _binaryData = bd;
        emit binaryDataChanged();
    }
}
